﻿
#include <time.h>
#include <stdio.h>

#define LP_CORE


#include "lp_thread.h"

#ifdef LP_WINDOWS
#else
#include <sys/time.h>
#ifndef pthread_cancel
#define pthread_cancel(T) (T)
#endif
#endif

LP_API int
lpNewThread (LPThread * t,
    void *(*start_fn)(void *), void * arg, int detach){
#ifdef LP_WINDOWS
    SECURITY_ATTRIBUTES psa;
    psa.nLength = sizeof(sizeof(psa));
    psa.bInheritHandle = detach;
    psa.lpSecurityDescriptor = NULL;
    HANDLE h = CreateThread(&psa, 0, (LPTHREAD_START_ROUTINE) start_fn, arg, 0, t);
    if (h == NULL)
        return -1;
    CloseHandle(h);
    return 0;
#else
    int err = pthread_create(t, NULL, start_fn, arg);
    return err == 0 ? 0 : -1;
#endif
}

LP_API void
lpExitThread (){
#ifdef LP_WINDOWS
    ExitThread(0);
#else
    pthread_exit(NULL);
#endif
}

LP_API int
lpCancelthread (LPThread t) {
#ifdef LP_WINDOWS
    HANDLE h = OpenThread(THREAD_ALL_ACCESS, FALSE, t);
    BOOL r =  TerminateThread(h, 0);
    CloseHandle(h);
    return r;
#else
    return pthread_cancel(t);
#endif
}

LP_API int
lpJoinThread (LPThread t) {
#ifdef LP_WINDOWS
    HANDLE h = OpenThread(THREAD_ALL_ACCESS, FALSE, t);
    DWORD r = WaitForSingleObject(h, INFINITE);
    CloseHandle(h);
    return r;
#else
    return pthread_join(t, NULL);
#endif
}

LP_API LPThread
lpSelfThread (){
#ifdef LP_WINDOWS
    return GetCurrentThreadId();
#else
    return pthread_self();
#endif
}

LP_API void
lpSleepms (uint32_t ms){
#ifdef LP_WINDOWS
    Sleep(ms);
#else
    //usleep(ms * 1000);
    struct timeval tv = {
        ms / 1000,
        (ms % 1000) * 1000,
    };
    // tv.tv_sec = (int)(ms/1000);
    // tv.tv_usec = (ms % 1000) * 1000000;
    select(0, NULL, NULL, NULL, &tv);
#endif
}



LP_API void
lpInitSpin(LPSpinLock * l) {
#ifdef LP_WINDOWS
    l->lock = FALSE;
#else
    pthread_spin_init(&l->lock, PTHREAD_PROCESS_PRIVATE);
#endif

}

LP_API int
lpLockSpin(LPSpinLock * l) {
#ifdef LP_WINDOWS
    while (InterlockedExchange(&l->lock, TRUE) == TRUE) {
        continue;
    }
    return 0;
#else
    return pthread_spin_lock(&l->lock);
#endif
}

LP_API int
lpTryLockSpin(LPSpinLock * l) {
#ifdef LP_WINDOWS
    return InterlockedExchange(&l->lock, TRUE) != TRUE;
#else
    return pthread_spin_trylock(&l->lock) == 0;
#endif
}

LP_API int
lpUnlockSpin(LPSpinLock * l) {
#ifdef LP_WINDOWS
    InterlockedExchange(&l->lock, FALSE);
    return 0;
#else
    return pthread_spin_unlock(&l->lock);
#endif
}

LP_API void
lpClearSpin(LPSpinLock * l) {
#ifdef LP_WINDOWS
    InterlockedExchange(&l->lock, FALSE);
#else
    pthread_spin_destroy(&l->lock);
#endif
}


LP_API void
lpInitMutex(LPMutex * l) {
#ifdef LP_WINDOWS
    InitializeCriticalSection(&l->lock);
#else
    pthread_mutex_init(&l->lock, NULL);
#endif
}

LP_API int
lpLockMutex(LPMutex * l) {
#ifdef LP_WINDOWS
    EnterCriticalSection(&l->lock);
    return 0;
#else
    return pthread_mutex_lock(&l->lock);
#endif
}

LP_API int
lpTryLockMutex(LPMutex * l){
#ifdef LP_WINDOWS
    TryEnterCriticalSection(&l->lock);
    return 0;
#else
    return pthread_mutex_trylock(&l->lock);
#endif
}

LP_API int
lpUnlockMutex(LPMutex * l) {
#ifdef LP_WINDOWS
    LeaveCriticalSection(&l->lock);
    return 0;
#else
    return pthread_mutex_unlock(&l->lock);
#endif
}


LP_API void
lpClearMutex(LPMutex * l) {
#ifdef LP_WINDOWS
    DeleteCriticalSection(&l->lock);
#else
    pthread_mutex_destroy(&l->lock);
#endif
}


LP_API void
lpInitRWLock (LPRWLock * l){
#ifdef LP_WINDOWS
    InitializeSRWLock(&l->lock);
#else
    pthread_rwlock_init(&l->lock, NULL);
#endif
}

LP_API void
lpClearRWLock(LPRWLock * l){
#ifdef LP_WINDOWS
    // DeleteCriticalSection(&l->lock);
#else
    pthread_rwlock_destroy(&l->lock);
#endif
}


LP_API void
lpLockRWLockR (LPRWLock * l){
#ifdef LP_WINDOWS
    AcquireSRWLockExclusive(&l->lock);
#else
    pthread_rwlock_rdlock(&(l->lock));
#endif
}

LP_API void
lpLockRWLockW (LPRWLock * l){
#ifdef LP_WINDOWS
    AcquireSRWLockShared(&l->lock);
#else
    pthread_rwlock_wrlock(&l->lock);
#endif
}

LP_API void
lpUnlockRWLockR(LPRWLock * l) {

#ifdef LP_WINDOWS
    ReleaseSRWLockExclusive(&l->lock);
#else
    pthread_rwlock_unlock(&l->lock);
#endif
}

LP_API void
lpUnlockRWLockW (LPRWLock * l){
#ifdef LP_WINDOWS
    ReleaseSRWLockShared(&l->lock);
#else
    pthread_rwlock_unlock(&l->lock);
#endif
}


LP_API void
lpInitCond (LPCond * cond){
#ifdef LP_WINDOWS
    InitializeConditionVariable(&cond->cond);
    InitializeCriticalSection(&cond->lock);
#else
    pthread_mutex_init(&cond->lock, NULL);
    pthread_cond_init(&cond->cond, NULL);
#endif
}

LP_API void
lpClearCond (LPCond * cond){

#ifdef LP_WINDOWS
    DeleteCriticalSection(&cond->lock);
#else
    pthread_mutex_destroy(&(cond->lock));
    pthread_cond_destroy(&(cond->cond));
#endif
}

LP_API int
lpLockCond (LPCond * cond){
#ifdef LP_WINDOWS
    EnterCriticalSection(&cond->lock);
    return 0;
#else
    return pthread_mutex_lock(&(cond->lock));
#endif
}

LP_API int
lpTryLockCond (LPCond * cond){
#ifdef LP_WINDOWS
    return TryEnterCriticalSection(&cond->lock);
#else
    return pthread_mutex_trylock(&cond->lock);
#endif
}

LP_API int
lpUnlockCond (LPCond * cond){
#ifdef LP_WINDOWS
    LeaveCriticalSection(&cond->lock);
    return 0;
#else
    return pthread_mutex_unlock(&cond->lock);
#endif
}

LP_API int
lpWaitCond (LPCond * cond){
#ifdef LP_WINDOWS
    return SleepConditionVariableCS(&cond->cond, &cond->lock, INFINITE);
#else
    return pthread_cond_wait(&cond->cond, &cond->lock);
#endif
}

LP_API int
lpWaitCondTimeout (LPCond * cond, uint32_t ms){
#ifdef LP_WINDOWS
    return SleepConditionVariableCS(&cond->cond, &cond->lock, ms);
#else
    struct timeval now;
    gettimeofday(&now, NULL);
    struct timespec to;
    to.tv_sec = now.tv_sec + (int)(ms/1000);
    to.tv_nsec = (now.tv_usec * 1000) + (ms % 1000) * 1000000;
    return pthread_cond_timedwait(&cond->cond, &cond->lock, &to);
#endif
}

LP_API int
lpSignalCond (LPCond * cond){
#ifdef LP_WINDOWS
    WakeConditionVariable(&cond->cond);
    return 0;
#else
    return pthread_cond_signal(&cond->cond);
#endif
}

LP_API int
lpBroadcastCond (LPCond * cond){
#ifdef LP_WINDOWS
    WakeAllConditionVariable(&cond->cond);
    return 0;
#else
    return pthread_cond_broadcast(&cond->cond);
#endif
}
